﻿using System.ComponentModel;
using System.Linq;
using System.Text;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using OpenRiaServices.DomainServices.Client;
using OpenRiaServices.Issue92.Helpers;
using OpenRiaServices.Issue92.Web.Services;

namespace OpenRiaServices.Issue92.ViewModel
{
    public class MainViewModel : ViewModelBase, IDataErrorInfo
    {
        private readonly RelayCommand _doBenchmarkCommand;
        private readonly RelayCommand _clearLogCommand;
        private bool _isBusy = false;
        private readonly StringBuilder _logBuilder = new StringBuilder();
        private string _numberOfChildEntitiesText;

        public MainViewModel()
        {
            this._doBenchmarkCommand = new RelayCommand(DoBenchmark, CanDoBenchmark);
            this._clearLogCommand = new RelayCommand(ClearLog);
        }

        public RelayCommand DoBenchmarkCommand
        {
            get
            {
                return this._doBenchmarkCommand;
            }
        }

        public RelayCommand ClearLogCommand
        {
            get
            {
                return this._clearLogCommand;
            }
        }

        // TODO: It might be rewritten as IValueConverter
        public string NumberOfChildEntitiesText
        {
            get
            {
                return this._numberOfChildEntitiesText;
            }

            set
            {
                Set(() => NumberOfChildEntitiesText, ref this._numberOfChildEntitiesText, value);
                this._doBenchmarkCommand.RaiseCanExecuteChanged();
            }
        }

        public bool IsBusy
        {
            get
            {
                return this._isBusy;
            }

            set
            {
                if (this._isBusy != value)
                {
                    this._isBusy = value;
                    RaisePropertyChanged(nameof(IsBusy));
                    this._doBenchmarkCommand.RaiseCanExecuteChanged();
                }
            }
        }

        public string Log
        {
            get
            {
                return this._logBuilder.ToString();
            }
        }

        private async void DoBenchmark()
        {
            this.IsBusy = true;
            try
            {
                var numberOfChildEntitiesText = int.Parse(NumberOfChildEntitiesText);
                var domainContext = new Issue92Context();
                var stopwatch = new Stopwatch();

                stopwatch.Start();
                AppendToLog("Loading data from service...");
                await domainContext.LoadAsync(domainContext.GetParentEntitiesQuery().Take(1), LoadBehavior.MergeIntoCurrent);
                if (domainContext.ParentEntities.Any())
                {
                    var parent = domainContext.ParentEntities.First();
                    await domainContext.LoadAsync(domainContext.GetChildEntitiesQuery().Where(ce => ce.ParentEntityId == parent.Id).Take(numberOfChildEntitiesText), LoadBehavior.MergeIntoCurrent);
                    stopwatch.Stop();
                    AppendToLog("Data loaded from service in: {0}", stopwatch.Elapsed.ToString());

                    stopwatch.Reset();
                    stopwatch.Start();
                    AppendToLog("Number of parent entities: {0}", domainContext.ParentEntities.Count);
                    AppendToLog("Number of child entities: {0}", domainContext.ChildEntities.Count);

                    stopwatch.Start();
                    AppendToLog("Accessing ChildEntities for parent id: {0} first time", parent.Id);
                    // !!! Problem is here
                    // Count invokes Load() in EntityCollection<T> which can take very long
                    var childrenCount = parent.ChildEntities.Count;
                    stopwatch.Stop();
                    AppendToLog("Time elapsed: {0} ms, number of child entities: {1}", stopwatch.ElapsedMilliseconds, childrenCount);
                    stopwatch.Reset();
                    stopwatch.Start();
                    // Entities have already been loaded so it will take very little time
                    AppendToLog("Accessing ChildEntities for parent id: {0} second time", parent.Id);
                    childrenCount = parent.ChildEntities.Count;
                    stopwatch.Stop();
                    AppendToLog("Time elapsed: {0} ms, number of child entities: {1}", stopwatch.ElapsedMilliseconds, childrenCount);
                }
                else
                {
                    AppendToLog("WARN: No parent entities found!");
                }
            }
            finally
            {
                this.IsBusy = false;
            }
        }

        private bool CanDoBenchmark()
        {
            return !this.IsBusy && string.IsNullOrEmpty(this[nameof(NumberOfChildEntitiesText)]);
        }

        private void ClearLog()
        {
            this._logBuilder.Clear();
            RaisePropertyChanged(nameof(Log));
        }

        private void AppendToLog(string message, params object[] args)
        {
            this._logBuilder.AppendFormat(message, args);
            this._logBuilder.AppendLine();
            RaisePropertyChanged(nameof(Log));
        }


        #region IDataErrorInfo Members

        public string Error
        {
            get
            {
                return string.Empty;
            }
        }

        public string this[string columnName]
        {
            get
            {
                if (columnName == nameof(NumberOfChildEntitiesText))
                {
                    int convertedValue;
                    if (!int.TryParse(NumberOfChildEntitiesText, out convertedValue))
                    {
                        return "Invalid number";
                    }
                }

                return null;
            }
        }

        #endregion
    }
}
